import java.io.IOException;
import java.util.*;

interface IPass{
    boolean Pass(int a);
}

//Метод управления
interface IControl{
    void control();
}
/*
* TUI, который одновременно является и посредником между основными классами
*/
public class Usersitter {
    private static final Scanner scanner = new Scanner(System.in);
    private static final HashMap<Integer, YearlyRecords> accounting = new HashMap<>();

    private static final TreeMap<String, IControl> controlDictionary = new TreeMap<>(Map.ofEntries(
            Map.entry("1", Usersitter::addMonths), Map.entry("2", Usersitter::addYear),
            Map.entry("3", Usersitter::checkAccounting), Map.entry("4", Usersitter::getMonthData),
            Map.entry("5", Usersitter::yearData), Map.entry("test launch", Usersitter::testLaunch),
            Map.entry("lazy test launch", Usersitter::lazyTestLaunch)
    ));

    //Функция для получения методов управления
    private static IControl getControl(String key) {
        if (!controlDictionary.containsKey(key)) return null;
        return controlDictionary.get(key);
    }
    private static void touchAccount(int year) {
        if (!accounting.containsKey(year)) {
            accounting.put(year, new YearlyRecords());
        }
    }
    private static void completeLaunch(String path, int year){
        //Ввод месяцев
        String template = getTemplate("m", year);
        ArrayList<FileCSV> files = getFiles(path, template);
        for (FileCSV file : files) {
            touchAccount(year);
            Pair<Integer, ArrayList<Tuple>> sendingData =
                    new Pair<>(getNumMonth(file.getName()), file.getArray());
            accounting.get(year).addMonthlyList(sendingData);
        }
        //Ввод года
        template = getTemplate("y", year);
        files = getFiles(path, template);
        for (FileCSV file : files) {
            touchAccount(year);
            ArrayList<Tuple> yearlyAcc = file.getArray();
            accounting.get(year).AddYearlyList(yearlyAcc);
        }
        System.out.printf(String.format("%42s%n","").replace(' ','+'));
        //Сверка
        System.out.printf(accounting.get(year).compareData());
        System.out.printf(String.format("%42s%n","").replace(' ','+'));
        //Данные по месяцам
        System.out.printf(accounting.get(year).getMonthlyData());
        System.out.printf(String.format("%42s%n","").replace(' ','+'));
        //Данные по году
        System.out.printf(accounting.get(year).getYearlyData());
        System.out.printf("%n+++ИМПЕРАТОР ЗАЩИЩАЕТ+++%n");
    }
    private static void testLaunch() {
        String path = getPath();
        int year = getYear();
        completeLaunch(path, year);
    }
    private static void lazyTestLaunch() {
        completeLaunch(Main.TEST_DIRECTORY, Main.TEST_YEAR);
    }

    //Сверка бухгалтерии
    private static void checkAccounting() {
        int year = getYear();
        if (!accounting.containsKey(year)) {
            System.out.printf("Данных за %d год нет%n", year);
            return;
        }
        System.out.printf(accounting.get(year).compareData());
    }
    //Получение данных по месяцам
    private static void getMonthData() {
        int year = getYear();
        if (!accounting.containsKey(year)) {
            System.out.printf("Данных за %d нет%n", year);
            return;
        }
        System.out.printf(accounting.get(year).getMonthlyData());
    }
    //Получение данных по году
    private static void yearData() {
        int year = getYear();
        if (!accounting.containsKey(year)) {
            System.out.printf("Данных за %d нет%n", year);
            return;
        }
        System.out.printf(accounting.get(year).getYearlyData());
    }
    //Возвращает файлы, считывая специфическую информацию
    private static ArrayList<FileCSV> setPathAndGetFiles(String type, int year) {
        String template = getTemplate(type, year);
        String path = getPath();
        return getFiles(path, template);
    }
    //Получает номер месяца из названия файла
    //В общем коряво, да и захардкоджен единственный формат имени файла, но увы.
    private static int getNumMonth(String filename) {
        int lastChar = filename.lastIndexOf(".");
        return Integer.parseInt(filename.substring(lastChar - 2, lastChar)) - 1;
    }
    //Добавление месячных отчётов
    private static void addMonths() {
        int year = getYear();
        ArrayList<FileCSV> files = setPathAndGetFiles("m", year);
        if (files == null) return;
        for (FileCSV file : files) {
            touchAccount(year);
            Pair<Integer, ArrayList<Tuple>> sendingData =
                    new Pair<>(getNumMonth(file.getName()), file.getArray());
            accounting.get(year).addMonthlyList(sendingData);
        }
    }
    //Добавление годовых отчётов
    private static void addYear() {
        int year = getYear();
        ArrayList<FileCSV> files = setPathAndGetFiles("y", year);
        if (files == null) return;
        for (FileCSV file : files) {
            touchAccount(year);
            ArrayList<Tuple> yearlyAcc = file.getArray();
            accounting.get(year).AddYearlyList(yearlyAcc);
        }
    }
    public static void start() {
        System.out.println("Запуск в " + System.getProperty("user.dir"));
        loop();
    }

    private static void loop() {
        while (true) {
            System.out.printf(
                    String.format("%42s%n","").replace(' ','+') +
                    "Выберите действие:%n" +
                    "1 - Считать все месячные отчёты%n" +
                    "2 - Считать годовой отчёт%n" +
                    "3 - Сверить отчёты%n" +
                    "4 - Вывести информацию о всех месячных отчётах%n" +
                    "5 - Вывести информацию о годовом отчёте%n" +
                    "0 - Выход%n"
            );
            String key = scanner.nextLine();
            if (key.equals("0")) return;
            IControl func = getControl(key);
            if (func == null) {
                System.out.println("Неизвестный ключ");
                continue;
            }
            func.control();
        }
    }
    //Получает файлы из фабрики
    private static ArrayList<FileCSV> getFiles(String path, String template) {
        ArrayList<FileCSV> ret;
        try {
            ret = FactoryCSV.getFiles(path, template);
            return ret;
        } catch (IOException exception) {
            System.err.println(exception.getMessage());
            return null;
        }
    }
    private static String getPath() {
        System.out.printf("Введите путь к файлу или директории%n");
        return scanner.nextLine();
    }
    //Получает шаблон для регулярки зависимо от переданной буквы
    //Захардкоджено
    private static String getTemplate(String type, int year) {
        StringBuilder template = new StringBuilder(type);
        template.append(".");
        template.append(year);
        if(type.equals("m")){
            template.append("((0[1-9])|(1[0-2]))"); //Немножко тёмной магии
        }
        template.append(".csv");
        return template.toString();
    }
    private static int getYear() {
        return getNumber((int a) -> a >= 0);
    }
    //Осталось со спринта 1
    private static int getNumber(IPass compare) {
        while (true) {
            System.out.printf("Введите год целым числом%n");
            int ret = Integer.parseInt(scanner.nextLine());
            if (compare.Pass(ret)) return ret;
            System.out.println("Неправильный ввод.");
        }
    }
}